package org.budo.ocr.service.standard;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;

import javax.imageio.ImageIO;

import org.budo.ocr.service.api.BudoOcrService;
import org.budo.ocr.service.api.OcrRequest;
import org.budo.ocr.service.api.OcrResponse;
import org.budo.ocr.service.impl.DefaultOcrResponse;
import org.budo.support.java.awt.robot.BudoImage;
import org.budo.support.java.awt.robot.BudoImageImpl;
import org.budo.support.lang.util.IoUtil;
import org.budo.support.lang.util.ListUtil;
import org.springframework.beans.factory.InitializingBean;

import com.tj.ocr.similar.TextSearcher;
import com.tj.ocr.util.OcrUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * @author limingwei
 */
@Slf4j
public class BudoStandardOcrService implements BudoOcrService, InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        ClassLoader classLoader = this.getClass().getClassLoader();

        OcrUtil.tsMySelf = this._loadResource(classLoader.getResource("outtsv1_m.bin").openStream());
        OcrUtil.tsFriends = this._loadResource(classLoader.getResource("outtsv1_f.bin").openStream());
    }

    private void assertSetUp() throws Exception {
        if (null == OcrUtil.tsMySelf || null == OcrUtil.tsFriends) {
            this.afterPropertiesSet();
        }
    }

    @Override
    public OcrResponse recognition(OcrRequest request) {
        try {
            byte[] byteArray = request.getByteArray();

            // 右侧消息截取核心部分
            byteArray = this.subRightMessage(byteArray);

            BufferedImage image = ImageIO.read(new ByteArrayInputStream(byteArray));

            if (null == image) {
                log.error("#65 image is null, request=" + request);
                return null;
            }

            // assertSetUp
            this.assertSetUp();

            String word = OcrUtil.ocrMySelf(image);

            if (null == word || word.isEmpty()) {
                // IoUtil.write(new BudoImageImpl(new ByteArrayInputStream(byteArray)).toPng(), new File("/dddd.png"));
            }

            return new DefaultOcrResponse(ListUtil.toList(word));
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    private TextSearcher _loadResource(InputStream inputStream) {
        try {
            return (TextSearcher) new ObjectInputStream(inputStream).readObject();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            IoUtil.close(inputStream);
        }
    }

    private byte[] subRightMessage(byte[] byteArray) {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArray);
        BudoImage budoImage = new BudoImageImpl(inputStream);

        int width = budoImage.width();

        int middle_y = budoImage.height() / 2;

        // 聊天背景灰色
        int _BACK_GREY = -657931;

        boolean in = false;
        int from = 0;
        for (int x = 50; x < width; x++) { // 左边50个像素是头像
            int rgb = budoImage.rgb(x, middle_y);
            if (_BACK_GREY != rgb && !in) {
                in = true;
                from = x;
                continue;
            }

            if (_BACK_GREY == rgb && in) {
                int x2 = from + 5;
                int w = x - from - 15;
                if (w <= 0) {
                    log.debug("#93 w=" + w + ", x=" + x + ", from=" + from);
                    continue;
                }

                // 通过文字底色得到文字区域的上界和高度
                int[] y2_and_h2 = this.y2_and_h2(budoImage, x2);

                BudoImage sub = budoImage.subImage(x2, y2_and_h2[0], w, y2_and_h2[1]);
                // IoUtil.write(sub.toPng(), new File("/subsub.png"));
                return sub.toPng(); // 返回截取后的部分
            }
        }

        return byteArray; // 返回本身
    }

    private int[] y2_and_h2(BudoImage budoImage, int x2) {
        int from = 0;
        boolean in = false;

        int height = budoImage.height();
        for (int y = 0; y < height; y++) {
            int rgb = budoImage.rgb(x2, y);
            if (!in && -1 == rgb) {
                from = y;
                in = true;
                continue;
            }

            if (in && -1 != rgb //
                    && y - from > 15) { // 限定文字最小高度
                return new int[] { from + 4, y - from - 4 };
            }
        }

        return new int[] { 0, height };
    }
}